#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <fstream>
#include <mariadb/mysql.h>
#include <cstdio>
#include <time.h>
#include <jsoncpp/json/json.h>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

#define DEBUG 0
#define INFO 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
#define DEFAULT_LEVEL DEBUG

#define LOG(level, format, ...)                                                              \
    do                                                                                       \
    {                                                                                        \
        if (level < DEFAULT_LEVEL)                                                           \
            break;                                                                           \
        time_t t = time(NULL);                                                               \
        struct tm *tm = localtime(&t);                                                       \
        char buffer[32] = {0};                                                               \
        strftime(buffer, 31, "%Y-%m-%d %H:%M:%S", tm);                                       \
        fprintf(stdout, "[%s][%s:%d]" format "\n", buffer, __FILE__, __LINE__, ##__VA_ARGS__); \
    } while (0);

#define debug(format, ...) LOG(DEBUG, format, ##__VA_ARGS__)
#define info(format, ...) LOG(INFO, format, ##__VA_ARGS__)
#define warning(format, ...) LOG(WARNING, format, ##__VA_ARGS__)
#define error(format, ...) LOG(ERROR, format, ##__VA_ARGS__)
#define fatal(format, ...) LOG(FATAL, format, ##__VA_ARGS__)


#define HOST "127.0.0.1"
#define PORT 3306
#define USER "root"
#define PASSWORD "Xsc.200411"

class mysql_util
{
public:
    static MYSQL *mysql_init_handle(const std::string &host,
                      uint16_t port,
                      const std::string &user,
                      const std::string &password,
                      const std::string &dbname)
    {
        // 1.获取mysql操作句柄
        MYSQL *mysql = mysql_init(nullptr);
        if(mysql == nullptr) {
            error("%s->%s", "mysql_init error", mysql_error(mysql));
            return NULL;
        }    

        // 2.连接mysql服务器
        if(!mysql_real_connect(mysql, host.c_str(), user.c_str(), password.c_str(), dbname.c_str(), port, nullptr, 0)) {
            error("%s->%s", "mysql_real_connect error", mysql_error(mysql));
            return NULL;
        }
    
        // 3.设置客户端字符集
        if(!mysql_set_character_set(mysql, "utf8mb4_general_ci")) {
            error("%s->%s", "mysql_set_character_set error", mysql_error(mysql));
            return NULL;
        }

        // 4.选择要操作的数据库
        mysql_select_db(mysql, dbname.c_str());
        info("%s", "mysql_init success!!!");
        return mysql;
    }
    static bool mysql_exe(MYSQL* mysql, const std::string &sql)
    {
        int ret = mysql_query(mysql, sql.c_str());
        if(ret != 0) {
            error("%s\n%s", sql.c_str(), mysql_error(mysql));
            return false;
        }
        return true;
    }
    static void mysql_destory(MYSQL *mysql) { if(mysql) mysql_close(mysql); }
};

class json_util
{
public:
    static bool serialize(const Json::Value &root, std::string &json_string)
    {
        std::stringstream ss;
        Json::StreamWriterBuilder builder;
        std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
        int ret = writer->write(root, &ss);
        if(ret != 0) {
            error("%s", "serialize error");
            return false;
        }
        json_string = ss.str();
        return true;
    }
    static bool deserialize(const std::string json_string, Json::Value &root)
    {
        std::stringstream ss;
        Json::CharReaderBuilder builder;
        std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
        std::string err;
        if(!reader->parse(json_string.c_str(), json_string.c_str() + json_string.size(), &root, &err)) {
            return false;
        }
        return true;
    }
};

class string_util
{
public:
    static int split(const std::string &str, const std::string &sep, std::vector<std::string> &res)
    {
        size_t pos = 0, index = 0;
        while(index < str.size()) {
            pos = str.find(sep, index);
            if(pos == std::string::npos) { // 没有找到分隔符，说明从pos位置开始到结束位置都是有效字符，直接插入到结果数组中
                res.emplace_back(str.substr(index));
                break;
            }
            // 找到了分隔符，就将从index开始到pos位置的字符串切割出来，插入到res中
            // 有可能有多个分隔符连在一起的情况，我们需要进行判断，防止插入空串到res中
            if(index == pos) {
                index = pos + sep.size(); //更新下一次开始找的位置
                continue;
            }
            res.emplace_back(str.substr(index, pos - index));
            index = pos + sep.size(); // 下一次开始的位置要跳过分隔符
        }
        return res.size();
    }
};

class file_util
{
public:
    static bool read(const std::string &filename, std::string &body)
    {
        // 1. 打开文件
        std::ifstream ifs(filename, std::ios::binary);
        if(!ifs.is_open()) {
            error("open file error-> %s", filename.c_str());
            return false;
        }
        // 2.计算文件大小
        size_t file_size = 0;
        ifs.seekg(0, std::ios::end);
        file_size = ifs.tellg();
        ifs.seekg(0, std::ios::beg);
        body.resize(file_size);
        //std::cout << "文件大小为-> " << file_size << std::endl;
        // 3.读取文件
        ifs.read(&body[0], file_size);
        if(!ifs.good()) {
            error("read file error-> %s", filename.c_str());
            ifs.close();
            return false;
        }
        // 4.关闭文件
        ifs.close();
        return true;
    }
};
